package cn.bird.tabledrive;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import lombok.Data;
import lombok.Getter;

import java.util.*;

public class MatchUtil {

    /**
     * 这个匹配值表示，任何条件都可以匹配
     */
    public static final Pattern __ = Pattern.__;
    public static final Pattern nil = Pattern.NIL;

    public enum Pattern {
        __,
        NIL
    }

    @Data
    private abstract static class Wrapper {
        protected Set<Object> values = new HashSet<>();
        public void setValues(Object... os) {
            this.values.addAll(Arrays.asList(os));
        }
    }

    /**
     * 条件拼装器
     */
    public static Container conditions(Object... objects) {
        return new Container(objects);
    }

    /**
     * 匹配其中的一个
     */
    private static class Any extends Wrapper {
    }

    /**
     * 反向包装内容
     */
    private static class Inverse extends Wrapper {
    }

    /**
     * 匹配{@code os}中任何一个即可
     */
    public static Any any(Object... os) {
        Any any = new Any();
        any.setValues(os);
        return any;
    }

    /**
     * 不能匹配{@code os}中任何一个
     */
    public static Inverse inverse(Object... os) {
        Inverse inverse = new Inverse();
        inverse.setValues(os);
        return inverse;
    }

    private MatchUtil() {
    }

    public static <R> ConditionBuilder<R> getConditionBuilder() {
        return new ConditionBuilder<>();
    }

    /**
     * 多个条件判断
     */
    public static class ConditionBuilder<R> {
        protected ConditionBuilder() {
        }

        private final List<Container> containers = new ArrayList<>();
        private final List<R> rList = new ArrayList<>();

        public ConditionBuilder<R> put(R r, Object... objects) {
            Assert.notNull(r);
            containers.add(new Container(objects));
            rList.add(r);
            return this;
        }

        public ConditionBuilder<R> put(Container container, R r) {
            Assert.notNull(r);
            containers.add(container);
            rList.add(r);
            return this;
        }

        /**
         * 用于条件拼装
         */
        public Scratchpad<R> condition(Container container) {
            return new Scratchpad<>(container, this);
        }

        /**
         * 用于条件拼装
         */
        public Scratchpad<R> condition(Object... objects) {
            return new Scratchpad<>(new Container(objects), this);
        }

        public Optional<R> build(Object... objects) {
            if (containers.isEmpty()) {
                return Optional.empty();
            }
            for (int i = 0; i < objects.length; i++) {
                if (Objects.isNull(objects[i])) {
                    objects[i] = Pattern.NIL;
                }
            }
            Container input = new Container(objects);
            for (int i = 0; i < containers.size(); i++) {
                if (containers.get(i).match(input)) {
                    return Optional.ofNullable(rList.get(i));
                }
            }
            return Optional.empty();
        }
    }

    public static void main(String[] args) {
        MatchUtil.<Runnable>getConditionBuilder()
                .put(() -> System.out.println("a,d or b,c"), "a", any("d", "b"), "c")
                .put(() -> System.out.println("a,c,b"), "a", inverse(nil), "b")
                .put(() -> System.out.println("__,b,c"), __, "b", "c")
                .put(() -> System.out.println("others"), __, __, __)
                .build("a", "d", "b")
                .ifPresent(Runnable::run);

        MatchUtil.<Runnable>getConditionBuilder()
                .condition(conditions("a", any("d", "b"), "c")).result(() -> System.out.println("a,d or b,c"))
                .condition(conditions("a", inverse(nil), "b")).result(() -> System.out.println("a,c,b"))
                .condition(conditions(__, "b", "c")).result(() -> System.out.println("__,b,c"))
                .condition(conditions(__, __, __)).result(() -> System.out.println("others"))
                .build("a", "d", "b")
                .ifPresent(Runnable::run);

        MatchUtil.<Runnable>getConditionBuilder()
                .condition("a", any("d", "b"), "c").result(() -> System.out.println("a,d or b,c"))
                .condition("a", inverse(nil),       "b").result(() -> System.out.println("a,c,b"))
                .condition(__,  "b",                "c").result(() -> System.out.println("__,b,c"))
                .condition(__,            __,                 __).result(() -> System.out.println("others"))
                .build("a", "d", "b")
                .ifPresent(Runnable::run);

    }

    /**
     * 对象容器
     */
    public static class Container {
        final Object[] objects;

        public Container(Object... objects) {
            if (Objects.isNull(objects)
                    || ArrayUtil.hasNull(objects)
                    || objects.length == 0) {
                throw new IllegalArgumentException("初始化容器类失败");
            }
            this.objects = objects;
        }

        public boolean match(Object rq) {
            if (Objects.isNull(rq)
                    || !(rq instanceof Container container)) {
                return false;
            }
            Object[] others = container.objects;
            if (objects.length != others.length) {
                return false;
            }
            for (int i = 0; i < objects.length; i++) {
                Object object = objects[i];
                //任意 或者 相等
                if (object.equals(__) ||
                        object.equals(others[i])) {
                    continue;
                }
                //包含
                if (object instanceof Any any
                        && (any.getValues()).contains(others[i])) {
                    continue;
                }
                //反向判断 不等于
                if (object instanceof Inverse inverse) {
                    if (inverse.getValues().contains(others[i])) {
                        return false;
                    } else {
                        continue;
                    }
                }
                return false;
            }
            return true;
        }

    }

    //声明一个中间类，用于暂存条件Container和ConditionBuilder
    @Getter
    public static class Scratchpad<R> {
        protected Container container;
        protected ConditionBuilder<R> conditionBuilder;

        protected Scratchpad(Container container, ConditionBuilder<R> conditionBuilder) {
            this.container = container;
            this.conditionBuilder = conditionBuilder;
        }

        public ConditionBuilder<R> result(R r) {
            return conditionBuilder.put(container, r);
        }
    }

}
